/* 
  Copyright (c) SilverLining 2015
  Date:    2016-1-7
  File:    process.cpp  
  Description: 

*/

#include "stdafx.h"


using namespace m::alloc;
using namespace m::error;

namespace m
{
	process::process()
		: _PmWeak(nullptr),
		_NextProcess(nullptr),
		_LastProcess(nullptr),
		_CurMsg(nullptr),
		_StartMsg(nullptr),
		_ForceStop(false),
		_MessageThread(nullptr),
		_Read(0),
		_Faulted(false)
	{
	}

	process::~process()
	{
		if (_MessageThread != NULL)
		{
			CloseHandle(_MessageThread);
		}

		clear(_PmWeak);
		clear(_CurMsg);
	}

	void process::send(const wchar_t* message)
	{
		send(message, nullptr);
	}

	void process::send(const wchar_t* message, object* data)
	{
		assert(_PmWeak);
		if (_PmWeak)
		{
			ref_storage track1;
			process_manager* pm = lock<process_manager>(track1, _PmWeak);
			if (pm)
			{
				pm->send(this, message, data);
			}
		}
	}
	
	void process::send(const wchar_t* message, object* data, bool cancelLast)
	{
		assert(_PmWeak);
		if (_PmWeak)
		{
			ref_storage track1;
			process_manager* pm = lock<process_manager>(track1, _PmWeak);
			if (pm)
			{
				pm->send(this, message, data, cancelLast);
			}
		}
	}

	void process::Start(weak_ref* PmWeak, message* StartMsg)
	{
		assert(!_PmWeak && PmWeak);
		if (!_PmWeak && PmWeak)
		{
			assign(_PmWeak, PmWeak);
			_StartMsg = StartMsg;
			_MessageThread = CreateThread(NULL, 0, ProcessThread, this, 0, NULL);
			if (_MessageThread == NULL)
			{
				throw bad_result_exception(HRESULT_FROM_WIN32(GetLastError()), "Failed to start process.");
			}
		}
	}

	void process::Stop()
	{
		if (_MessageThread != NULL)
		{
			WaitForSingleObject(_MessageThread, INFINITE);
			CloseHandle(_MessageThread);
			_MessageThread = NULL;
			clear(_PmWeak);
			clear(_CurMsg);
		}
	}

	bool process::started() const
	{
		if (_ForceStop)
		{
			return false;
		}
		return true;
	}

	bool process::resumed() const
	{
		if (_PmWeak)
		{
			ref_storage track1;
			process_manager* pm = lock<process_manager>(track1, _PmWeak);
			if (pm)
			{
				return pm->resumed();
			}
		}
		return false;
	}

	void process::Enter()
	{
		m::error::LastHr = 0;

		std::exception_ptr eptr = nullptr;
		try
		{
			process_entry();
		}
		catch (...)
		{
			eptr = std::current_exception();
			DBGL(L"Unhandled exception in process: '%x'", eptr);
		}

		if (!_ForceStop)
		{
			// "We" are stopping ourself.
			send(L"us.sl.m.stop", this);
		}

		_Faulted = true;
		while (has_message())
		{
			// In the case we threw an exception, we want to read any remaining messages. 
			next_message();
		}

		assert(_PmWeak);
		if (_PmWeak)
		{
			ref_storage track1;
			process_manager* pm = lock<process_manager>(track1, _PmWeak);
			if (pm)
			{
				// We might get deleted here.
				pm->Remove(this);
			}
		}
		if (eptr)
		{
			std::rethrow_exception(eptr);
		}
	}

	static DWORD WINAPI ProcessThread(const LPVOID lParameter)
	{
		ref_storage track1;
		process* proc = reference(track1, static_cast<process*>(lParameter));
		proc->Enter();
		return 0;
	}

	bool process::next_message()
	{
		assert(_PmWeak);
		if (_PmWeak)
		{
			if (!_ForceStop)
			{
				ref_storage track1;
				process_manager* pm = lock<process_manager>(track1, _PmWeak);
				if (pm)
				{
					bool gotMsg;
					if (_StartMsg)
					{
						_CurMsg = _StartMsg;
						_StartMsg = nullptr;
						gotMsg = true;
					}
					else
					{
						gotMsg = pm->GetNext(&_CurMsg);
					}

					if (gotMsg)
					{
						assert(_CurMsg);
						if (_CurMsg)
						{
							++_Read;
							if (!_Faulted)
							{
								handle_message(_CurMsg);
							}
							if (_CurMsg->type_equals(L"us.sl.m.stop"))
							{
								if (_CurMsg->data() == nullptr || _CurMsg->data() == this)
								{
									_ForceStop = true;
								}
							}
						}
					}
					return gotMsg;
				}
			}
		}
		return false;
	}

	bool process::has_message() const
	{
		assert(_PmWeak);
		if (_PmWeak)
		{
			if (!_ForceStop)
			{
				ref_storage track1;
				process_manager* pm = lock<process_manager>(track1, _PmWeak);
				if (pm && _CurMsg)
				{
					return pm->HasMessageAbove(_CurMsg);
				}
			}
		}
		return false;
	}

	void process::set_periodic_timer(timer_state** Ts, const scalar& delay, const scalar& interval, const wchar_t* evt, object* data)
	{
		assert(Ts);
		if (Ts)
		{
			assert(_PmWeak);
			if (_PmWeak)
			{
				ref_storage track1;
				process_manager* pm = lock<process_manager>(track1, _PmWeak);
				if (pm)
				{
					pm->ChangeTimer(Ts, delay, interval, false, this, evt, data);
				}
			}
		}
	}

	void process::set_periodic_timer(timer_state** Ts, const scalar& delay, const scalar& interval, const wchar_t* evt)
	{
		set_periodic_timer(Ts, delay, interval, evt, nullptr);
	}

	void process::set_one_shot_timer(timer_state** Ts, const scalar& delay, const wchar_t* evt, object* data)
	{
		assert(Ts);
		if (Ts)
		{
			assert(_PmWeak);
			if (_PmWeak)
			{
				ref_storage track1;
				process_manager* pm = lock<process_manager>(track1, _PmWeak);
				if (pm)
				{
					pm->ChangeTimer(Ts, delay, 0., true, this, evt, data);
				}
			}
		}
	}

	void process::set_one_shot_timer(timer_state** Ts, const scalar& delay, const wchar_t* evt)
	{
		set_one_shot_timer(Ts, delay, evt, nullptr);
	}

	void process::clear_timer(timer_state** Ts)
	{
		assert(Ts);
		if (Ts)
		{
			assert(_PmWeak);
			if (_PmWeak)
			{
				ref_storage track1;
				process_manager* pm = lock<process_manager>(track1, _PmWeak);
				if (pm)
				{
					pm->ClearTimer(Ts);
				}
			}
		}
	}

}